/*
1.windows临界区
    和mutex很相似
    临界区变量代表不同的临界区
    代码如下
2.windows临界区多次进入实验
    在同一个线程中多次进入如临界区是可以的，但是必须以对应的次数离开临界区
    不同线程就会卡住等待
    但是相同的mutex对象不可以连续lock多次

自动析构技术
lock-guard<mutex>，自动锁定和解锁 
所以：
如果lock-guard两次也会报异常


3.互斥量
mutex：独占互斥量，只有一个线程可以拿到锁和上锁，解锁
recursive-mutex：递归独占互斥量，单个互斥量允许多次lock（）
    递归次数据说有限制，递归太多次数可以报异常，其他和mutex用法相同

4.带超时的互斥量，timed_mutex和recursive_timed_mutex
    比mutex多了两个新接口
    try-lock-for（）：等待一段时间来拿锁，如果规定实际内拿到锁，或等待超时（没拿到锁），
                    继续程序运行，而不会向mutex一样卡住

    try-lock-until():等到到时间点其他相同
    
*/


#include<iostream>
#include<thread>
#include<vector>
#include <list>
#include<mutex>
//临界区，引入头文件
#include<windows.h>
using namespace std;
//定义开关
#define __WINDOWSJQ_

//测试windows编程下的自动析构
//这种类被一些书籍成为RAII类（Resource Acquisition is initialization）资源获取即初始化
//容器，智能指针都属于RAII类
class CWinLock{
public：
    //构造函数进入临界区
    CWinLock(CRITICAL_SECTION *pCritmp)
    {
        m_pCritical=pCritmp;
        EnterCriticalSection(&m_pCritical);
    }
    //析构函数离开临界区
    ~CWinLock()
    {
        LeaveCriticalSection(&m_pCritical);
    }

}


class A {
public:

	//线程入口，读入消息队列
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; ++i) {
            cout<<"input a data="<<i<<endl;

#ifdef __WINDOWSJQ_

            //自动临界区
            //也可以调用多次
            CWinLock wlock(&my_winsec);
            CWinLock wlock2(&my_winsec);

            //进入临界区（可以多次）
            //EnterCriticalSection(&my_winsec);
            //EnterCriticalSection(&my_winsec);
            msgRecvQueue.push_back(i);
            //离开临界区（对应的次数）
            //LeaveCriticalSection(&my_winsec);
            //LeaveCriticalSection(&my_winsec);
#else

            //使用lock-guard
            //lock_guard<mutex> sbguard(my_mutex);

            //不可以
            //my_mutex.lock();
            //my_mutex.lock();
            //msgRecvQueue.push_back(i);
            //不可以
            //my_mutex.unlock();
            //my_mutex.unlock();

        //测试timed-mutex
        chrono::microseconds timeout(100);
        //if(my_mutexT.try_lock_for(timeout))
        //固定写法
        if(my_mutexT.try_lock_until(chrono::stead_clock::now()+timeout))
        {
            //等待100毫秒来拿锁，如果拿到
            msgRecvQueue.push_back(i);
            //用完了要解锁
            my_mutexT.unlock();

        }
        else{
            //没有拿到锁
            //做一些事，或者休息一下等待下一次拿锁
            chrono:;microseconds sleeptime(100);
            this_thread::sleep_for(sleeptime);    
        }





#endif
		}
	}
	//共享数据操作，提取到一个方法中
	bool outMsgLULProc(int &command) {

#ifdef __WINDOWSJQ_
    EnterCriticalSection(&my_winsec);
		if (!msgRecvQueue.empty()) {
			command=msgRecvQueue.front();
			msgRecvQueue.pop_front();
            LeaveCriticalSection(&my_winsec);
			return true;
		}
        LeaveCriticalSection(&my_winsec);
 

#else
        my_mutex.lock();
		if (!msgRecvQueue.empty()) {
            command=msgRecvQueue.front();
			msgRecvQueue.pop_front();
            my_mutex.unlock();
			return true;
		}
        my_mutex.unlock();
#endif
	}

	//线程入口，取出消息队列
	void outMsgRecvQueue() {
		
        int command=0;
		for (int i = 0; i < 100000; ++i) {
			if (outMsgLULProc(command)) {
				cout << "取出一个元素："<<command<<endl;
			}
			else {
				cout << "消息为空" << i << endl;
			}
		}
		cout << "消息处理结束" << endl;
	}

    //定义构造函数
    A(){   
    #ifdef __WINDOWSJQ_
        //初始化临界区（在用之前必须初始化）
        InitializeCriticalSection(&my_winsec);
    #endif
}


void testfunc1(){
    //lock_guard<mutex> sbguard (my_mutex);
    //干一些事
    //在test1中调用了test2
    testfunc2();
    //因为使用的锁相同，所以程序出现问题
    //利用recursive-mutex
    lock_guard<mutex> sbguard (my_mutexR);
}
void testfunc2(){
    //lock_guard<mutex> sbguard (my_mutex);
    //干一些事
    //利用recursive-mutex
    lock_guard<mutex> sbguard (my_mutexR);
}

private:
	list<int> msgRecvQueue;
	mutex my_mutex;
    recursive_mutex my_mutexR;
    timed_mutex my_mutexT;
    //创建临界区变量
    #ifdef __WINDOWSJQ_
        CRITICAL_SECTION my_winsec;
    #endif

};


int main(int argc,char *arcv[]){
    A a;
    thread myInMsgObj(&A::inMsgRecvQueue,&a);
    thread myOutMsgObj(&A::outMsgRecvQueue,&a);
    myInMsgObj.join();
    myOutMsgObj.join();
    return 0;


}